package com.atguigu.gmall.index.utils;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.core.script.DefaultRedisScript;
import org.springframework.stereotype.Component;

import java.util.Arrays;
import java.util.Timer;
import java.util.TimerTask;

@Component
public class DistributedLock {
    @Autowired
    StringRedisTemplate stringRedisTemplate;

    private Timer timer;
    //获取锁+可重入锁
    //redis.call('hincrby', KEYS[1],KEYS[2],1) 如果锁不存在添加锁status为1，锁已存在status+1
    public Boolean tryLock(String lockName,String uuid,Integer expire ){
        String script= "if(redis.call('exists',KEYS[1])==0 " +
                "or redis.call('hexists',KEYS[1],KEYS[2])==1) " +
                "then " +
                    "redis.call('hincrby', KEYS[1],KEYS[2],1) " +
                    "redis.call('expire',KEYS[1],ARGV[1]) " +
                "return 1 " +
                "else " +
                "return 0 " +
                "end";
        Boolean flag = stringRedisTemplate.execute(new DefaultRedisScript<>(script, Boolean.class),
                Arrays.asList(lockName, uuid), expire.toString());
        if(!flag){
            try {
                Thread.sleep(10);
                tryLock( lockName, uuid, expire);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        renewExpire( lockName, uuid, expire );//获取到锁之后，定时任务自动给这个锁续期
        return true;
    }
        //释放锁
    public void unlock(String lockName,String uuid) {
        String script = "if(redis.call('hexists',KEYS[1],KEYS[2])==0) " +
                "then " +
                "return nil " +
                "elseif(redis.call('hincrby',KEYS[1],KEYS[2],-1)==0) " +
                    "then " +
                    "return redis.call('del',KEYS[1]) " +
                    "else " +
                    "return 0 " +
                "end";
        //返回值有null，注意不能使用boolean类型，因为null会被翻译为false
        Long flag = stringRedisTemplate.execute(new DefaultRedisScript<>(script, Long.class),
                Arrays.asList(lockName, uuid));
        if (flag == null) {
            throw new RuntimeException("要解的锁不存在，或者不属于你");
        }else if(flag==1) {
            timer.cancel();//取消定时任务
        }

    }
    //自动续期
    public void renewExpire( String lockName,String uuid,Integer expire){
        String script="if(redis.call('hexists', KEYS[1], KEYS[2]) == 1) " +
                "then " +
                "return redis.call('expire', KEYS[1], ARGV[1]) " +
                "else " +
                "return 0 " +
                "end";
        timer = new Timer();
        timer.schedule(new TimerTask() {
            @Override
            public void run() {
            stringRedisTemplate.execute(new DefaultRedisScript<>(script,Boolean.class),Arrays.asList(lockName,uuid),expire);
            }
        },expire*1000/3,expire*1000/3);
    }
}

